
package redeS.visualizacao;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Formatter;
import peersim.cdsim.CDSimulator;
import peersim.cdsim.CDState;
import peersim.config.Configuration;
import peersim.core.Control;

/**
 * Gera a representação gráfica da rede através da linguagem graphdot do programa GraphViz
 * @author thiago-pc
 */
public class GraphDotVisualizer implements Control {

	/**
	 * O parâmetro num_images representa o número de imagens que essa classe irá gerar.
	 */
	public static String PAR_NIMG = "num_images";
	private int numImages;
	private final int defaultNumImages = 2;
	
	// Gerar imagem automaticamente?
	private String PAR_IMGAUTO = "image_auto";
	private boolean imageAuto;
	
	// O número do protocolo (é calculado internamente no simulador).
	private int pid = -1;
	
	private File dir, image;
	private Formatter output;
	private int imageIndex = 0;
	
	private P2PGraph g;
	
	// Quais ciclos o método execute vai rodar.
	private int[] cicles;
	private int lastCicle;
	private int nextCicle;
	
	public GraphDotVisualizer(String prefix){
		// Pega o valor do parâmetro numImages do arquivo de configuração.
		// Se o arquivo de configuração não tiver esse parâmetro ele será setado
		// com o valor padrão (veja o valor default na declaração da variável).
		numImages = Configuration.getInt(prefix + "." + PAR_NIMG,defaultNumImages);
		
		imageAuto = Configuration.getBoolean(prefix + "." + PAR_IMGAUTO, false);
		
		lastCicle = Configuration.getInt(CDSimulator.PAR_CYCLES);
		cicles = new int[numImages];
		Arrays.fill(cicles, -1);
		nextCicle = 0;
		
		if(Configuration.contains(prefix + ".protocol") ){
			pid = Configuration.getPid(prefix + ".protocol");
		}
		if(pid<0){
			System.err.println("Especifique a string identificadora do protocolo Linkable");
			System.exit(1);
		}
		g = new P2PGraph(pid);
		
		// Define os ciclos assim:
		// Se o usuário desejar só uma imagem, a classe gerará no começo da simulação,
		// se o usuário desejar duas imagens, a classe gerará também uma no final da simulação,
		// mas se o usuário desejar mais de duas imagens, a classe gerará, além das duas
		// acima, as outras em ciclos distribuídos mais ou menos de forma igual.
		if(numImages>0){
			cicles[0] = 0;
		}
		if(numImages>1){
			cicles[1] = lastCicle - 1;
		}
		if(numImages>2){
			int jump = lastCicle / numImages;
			int pos = jump;
			int cont = 2;
			
			while(cont<numImages){
				cicles[cont] = pos;
				pos+=jump;
				cont++;
			}
		}
		// Ordena os ciclos.
		Arrays.sort(cicles);
	}
	
	@Override
	public boolean execute() {
		// Se o ciclo atual estiver especificado dentro do array cicles, esse método rodará.
		if(nextCicle<cicles.length && cicles[nextCicle]==CDState.getCycle()){
			// Atualiza o grafo e incrementa a posição do vetor de ciclos que será lida.
			nextCicle++;
			
			System.out.println(this.getClass().getSimpleName() + ": escrevendo arquivo...");
			
			// Cria o diretório
			dir = new File(this.getClass().getPackage().getName());
			if(!dir.isDirectory()){
				System.out.println("\t\tdiretório " + dir.getPath() + " não encontrado: "+
						"criando diretório...");
				if(dir.mkdir()){
					System.out.println("\t\tdiretório criado com sucesso.");
				}
				else{
					System.out.println("\t\terro ao criar diretório");
					return true;
				}
			}
			
			// Cria o arquivo.
			image = new File(dir.getPath() + "/" + "imagem_" + imageIndex + ".dot");
			imageIndex++;
			try {
				output = new Formatter(image);
			} catch (Exception e) {
				e.printStackTrace();
				return true;
			}
			
			// Escreve no arquivo.
			output.format("graph G{\nnode [shape=circle, fontsize=8, width=.005];\n");
			for(int i=0; i<g.size(); i++){
				if(g.degree(i)>0){
					for(int j : g.getNeighboursArray(i)){
						if(g.isEdge(j, i) && i<j){
							output.format(i + "--" + j + ";\n");
						}
						if(!g.isEdge(j, i)){
							output.format(i + "->" + j + ";\n");
						}
					}
				}
				else{
					output.format(i + ";\n");
				}
			}
			output.format("}\n");
			output.close();
			System.out.println("\t\tescreveu arquivo " + image.getName() + " com sucesso");
		}
		if(imageAuto && CDState.getCycle() == lastCicle - 1){
			File exec = new File(this.getClass().getPackage().getName() + ".graphviz");
			System.out.println(this.getClass().getSimpleName() + ": fez chamada ao programa " +
					exec.getName());
			try {
				Runtime.getRuntime().exec(exec.getAbsolutePath());
			} catch (IOException e) {
				e.printStackTrace();
			}
			//
		}
		return false;
	}

}
